package com.ishitong.platform.common.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.ishitong.platform.common.page.Page;
import com.ishitong.platform.utils.BeanUtils;
import com.ishitong.platform.utils.GenericsUtils;


/**
 * 纯Hibernate Entity DAO基类。 通过泛型，子类无需扩展任何函数即拥有完整的CRUD操作。 子类在继承的基础上需要扩展业务接口。
 * 
 * @author liufeng
 * @see HibernateDaoSupport
 * @since3.0
 */

@SuppressWarnings("unchecked")
abstract public class GenericDao<SuperClass> extends GenericSQLDao implements GenericDaoInterface<SuperClass> {

	public static final int TRUE = 1;
	public static final int FALSE = 0;

	protected Log logger = LogFactory.getLog(getClass());

	/**
	 * Dao所管理的Entity类型.
	 */
	protected Class<SuperClass> entityClass;

	/**
	 * 取得entityClass的函数. 不支持泛型的子类可以抛开Class<PersistentClass>
	 * entityClass,重新实现此函数达到相同效果。
	 * 
	 * @return 被持久化的实体类的Class
	 */
	protected Class getEntityClass() {
		return entityClass;
	}

	/**
	 * 在构造函数中将泛型Persistent.class赋给entityClass
	 */
	public GenericDao() {
		entityClass = GenericsUtils.getGenericClass(getClass(), 0);
	}

	/**
	 * 通过主键取得实例
	 * 
	 * @param id
	 *            主键
	 * @return 该主键对应的实例
	 */
	public SuperClass get(Serializable id) {
		SuperClass o = (SuperClass) getHibernateTemplate().get(getEntityClass(), id);
		if (o == null)
			throw new ObjectRetrievalFailureException(getEntityClass(), id);
		return o;
	}

	/**
	 * 保存实例
	 * 
	 * @param obj
	 *            待保存的实例
	 */
	public void save(SuperClass obj) {
		try {
			Field f = obj.getClass().getDeclaredField("id"); 
			f.setAccessible(true);
			
			if (f.get(obj).toString().isEmpty()) {
				f.set(obj, null);
			}
			getHibernateTemplate().saveOrUpdate(obj);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/** 
	* 使用反射根据属性名称获取属性值  
	*  
	* @param  fieldName 属性名称 
	* @param  o 操作对象 
	* @return Object 属性值 
	*/  
	
	public void saveOnly(SuperClass obj) {
		getHibernateTemplate().save(obj);
	}

	/**
	 * 根据主键删除实例
	 * 
	 * @param id
	 *            带删除实例的主键
	 */
	public void remove(Serializable id) {
		remove(get(id));
	}

	/**
	 * 删除指定的实例
	 * 
	 * @param obj
	 *            待删除的实例
	 */
	public void remove(SuperClass obj) {
		getHibernateTemplate().delete(obj);
	}

	/**
	 * 消除与 Hibernate Session 的关联
	 * 
	 * @param obj
	 *            待操作的实例
	 */
	public void evit(SuperClass obj) {
		getHibernateTemplate().evict(obj);
	}

	/**
	 * 分页取得所有记录
	 * 
	 * @param pageSize
	 *            每页记录数
	 * @param pageNum
	 *            待取得页码，从1开始
	 * @param orderBy
	 *            排序字段
	 * @param isAsc
	 *            是否升序，false表示降序
	 * @return 指定页码的记录
	 */
	public Page listAll(int pageSize, int pageNum, String orderBy, boolean isAsc) {
		// 默认按id排序
		if (orderBy == null || "".equals(orderBy)) {
			orderBy = "id";
		}

		if (isAsc)
			return findByDetachedCriteria(DetachedCriteria.forClass(entityClass).addOrder(Order.asc(orderBy)), pageSize, pageNum);
		else
			return findByDetachedCriteria(DetachedCriteria.forClass(entityClass).addOrder(Order.desc(orderBy)), pageSize, pageNum);
	}

	/**
	 * 分页查询指定字段的值等于指定值的记录
	 * 
	 * @param fieldName
	 *            字段名称
	 * @param value
	 *            指定的值
	 * @param pageSize
	 *            每页记录数
	 * @param pageNum
	 *            页号，从1开始
	 * @return 指定页的记录
	 */
	public Page findByFieldEquals(String fieldName, Object value, int pageSize, int pageNum) {
		Assert.hasText(fieldName);
		Assert.notNull(value);
		Criteria criteria = this.getCriteria();
		criteria.add(Restrictions.eq(fieldName, value));
		return this.findByCriteria(criteria, pageSize, pageNum);
	}

	/**
	 * 通过hql查询记录集。<br>
	 * 此方法含有可变参数args，可用如下四种方法调用：
	 * <ul>
	 * <li>dao.findByHql(hql, pageSize, pageNum);
	 * <li> dao.findByHql(hql, pageSize, pageNum, arg0);
	 * <li> dao.findByHql(hql, pageSize, pageNum, arg0, arg1);
	 * <li> dao.findByHql(hql, pageSize, pageNum, new Object[arg0, arg1, arg2]);
	 * </ul>
	 * pageSize和pageNum必须同时大于0才有效，否则将取出所有符合条件的记录。
	 * 
	 * @param hql
	 *            查询语句
	 * @param pageSize
	 *            每页记录数
	 * @param pageNum
	 *            页码，从1开始
	 * @param args
	 *            查询语句参数
	 * @return 该页的Page对象
	 * @see com.ishitong.platform.common.page.Page
	 */
	public Page findByHql(final String hql, final int pageSize, int pageNum, final Object... args) {
		Assert.hasText(hql);
		// 创建查询
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < args.length; i++) {
			query.setParameter(i, args[i]);
		}

		// 为了减少一次对于createQuery的调用，所以不调用this.count方法
		long totalCount = 0l;
		if (pageSize > 0 && pageNum > 0) {
			String countQueryString = " select count(*) " + this.removeSelect(removeOrders(hql));
			List countlist = getHibernateTemplate().find(countQueryString, args);
			if (countlist != null && countlist.size() >= 1)
				totalCount = (Long) countlist.get(0);
			// 返回分页对象
			if (totalCount < 1)
				return new Page(pageNum,pageSize,0,null);
		}
		
		if (pageNum > 0 && pageSize > 0) {
			pageNum=super.getRealPageNum(totalCount, pageSize, pageNum);
			int startIndex = (pageNum - 1) * pageSize;
			query.setFirstResult(startIndex).setMaxResults(pageSize);
		}
		List list = query.list();
		if (pageSize == 0 || pageNum == 0) {
			totalCount = list.size();
		}
		return new Page(pageNum, pageSize, totalCount, list);
	}

	public List findFastByHql(final String hql,final int pageSize,final int pageNum,final Object...args)
	{
	  Assert.hasText(hql);
    Query query = getSession().createQuery(hql);
    for (int i = 0; i < args.length; i++) {
      query.setParameter(i, args[i]);
    }
    if (pageNum > 0 && pageSize > 0) {
      int startIndex = (pageNum - 1) * pageSize;
      query.setFirstResult(startIndex).setMaxResults(pageSize);
    }
    return query.list();
	}
	
	public List findFastByHql(final String hql, final Object... args) {
		Assert.hasText(hql);
		// 创建查询
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < args.length; i++) {
			query.setParameter(i, args[i]);
		}
		return query.list();
	}
	
	public SuperClass findOneByHql(final String hql, final Object... args) {
    Assert.hasText(hql);
    // 创建查询
    Query query = getSession().createQuery(hql);
    for (int i = 0; i < args.length; i++) {
      query.setParameter(i, args[i]);
    }
    List result=query.list();
    if(!result.isEmpty())
    {
      return (SuperClass)result.get(0);
    }
    return null;
  }

	/**
	 * 分页查询函数，使用Criteria.
	 * 
	 * @param criteria
	 *            查询用Criteria
	 * @param pageSize
	 *            每页的记录数
	 * @param pageNum
	 *            页码，从1开始
	 * @return 指定页码的记录
	 * @see com.ishitong.platform.common.page.Page
	 */
	public Page findByCriteria(Criteria criteria, int pageSize, int pageNum) {
		Assert.notNull(criteria);
		CriteriaImpl impl = (CriteriaImpl) criteria;

		// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
		Projection projection = impl.getProjection();
		List<CriteriaImpl.OrderEntry> orderEntries;
		try {
			orderEntries = (List) BeanUtils.getPrivateProperty(impl, "orderEntries");
			BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		long totalCount = 0;
		// 执行查询
		if (pageSize > 0 && pageNum > 0) {
			totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
			if (totalCount < 1)
				return new Page(pageNum,pageSize,0,null);
		}
		// 将之前的Projection和OrderBy条件重新设回去
		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		if (pageNum > 0 && pageSize > 0) {
			pageNum=super.getRealPageNum(totalCount, pageSize, pageNum);
			int startIndex = (pageNum - 1) * pageSize;
			criteria.setFirstResult(startIndex).setMaxResults(pageSize);
		}
		List list = criteria.list();
		if (pageSize == 0 || pageNum == 0) {
			totalCount = list.size();
		}
		return new Page(pageNum, pageSize, totalCount, list);
	}

	public List findFastByCriteria(Criteria criteria) {
		Assert.notNull(criteria);
		return criteria.list();
	}

	/**
	 * 分页查询函数，使用DetachedCriteria
	 * 
	 * @param dc
	 *            设置好条件的DetachedCriteria
	 * @param pageSize
	 *            每页的记录数
	 * @param pageNum
	 *            页码，从1开始
	 * @return 指定页码的记录
	 */
	public Page findByDetachedCriteria(DetachedCriteria dc, int pageSize, int pageNum) {
		Assert.notNull(dc);
		Criteria criteria = dc.getExecutableCriteria(this.getSession());
		return this.findByCriteria(criteria, pageSize, pageNum);
	}

	public List findFastByDetachedCriteria(DetachedCriteria dc) {
		Assert.notNull(dc);
		Criteria criteria = dc.getExecutableCriteria(this.getSession());
		return this.findFastByCriteria(criteria);
	}

	public int executeByHql(String hql, Object... args) {
		Query query = super.getSession().createQuery(hql);
		for (int i = 0; i < args.length; i++) {
			query.setParameter(i, args[i]);
		}
		return query.executeUpdate();
	}

	/**
	 * 统计查询语句的结果集大小。<br>
	 * 此方法含有可变参数args，可用如下四种方法调用：
	 * <ul>
	 * <li>dao.count(hql);
	 * <li> dao.count(hql, arg0);
	 * <li> dao.count(hql, arg0, arg1);
	 * <li> dao.count(hql, new Object[arg0,arg1,arg2]);
	 * </ul>
	 * 
	 * @param hql
	 *            查询语句
	 * @param args
	 *            绑定的参数
	 * @return 结果集的大小
	 */
	public long count(final String hql, final Object... args) {
		Assert.hasText(hql);
		String countQueryString = " select count(*) " + this.removeSelect(this.removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, args);
		long totalCount = 0l;
		if (countlist != null && countlist.size() >= 1)
		{
			totalCount = (Long) countlist.get(0);
		}
		return totalCount;
	}

	/**
	 * 取得和被持久化的EntityClass关联的Criteria实例。
	 * <p>
	 * 此方法仅供业务层使用，例如其它继承与GenericDao的Manager类。
	 * 
	 * @return 持久化类的Criteria对象
	 */
	public Criteria getCriteria() {
		return getSession().createCriteria(getEntityClass());
	}

	/**
	 * 取得被持久化的EntityClass关联的DetachedCriteria。<br>
	 * 此方法供业务层之上的上层代码使用，如Struts的Action类或者JUnit的测试代码。
	 * 
	 * @return 关联的DetachedCriteria
	 */
	public DetachedCriteria getDetachedCriteria() {
		return DetachedCriteria.forClass(getEntityClass());
	}

	public boolean isNotUnique(Object entity, String... names) {
		Criteria criteria = getSession().createCriteria(entity.getClass()).setProjection(Projections.rowCount());
		try {
			for (String name : names) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
			}

			String keyName = getSessionFactory().getClassMetadata(entity.getClass()).getIdentifierPropertyName();
			if (keyName != null) {
				Object id = PropertyUtils.getProperty(entity, keyName);
				// 如果是update,排除自身
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(keyName, id)));
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			return false;
		}
		return ((Integer) criteria.uniqueResult()) > 0;
	}

	/**
	 * 查询分页数据
	 * 
	 * @param pageSize
	 * @param pageNum
	 * @return
	 */
	public Page find(int pageSize, int pageNum) {
		Criteria criteria = this.getCriteria();
		return this.findByCriteria(criteria, pageSize, pageNum);
	}

	/**
	 * 根据唯一字段值ID查询数据
	 * 
	 * @param id
	 * @return
	 */
	public SuperClass findByUniqueKey(String fieldName, Object value) {
		Page page = this.findByFieldEquals(fieldName, value, 0, 0);
		if (!page.getResults().isEmpty()) {
			return (SuperClass) page.getResults().get(0);
		}
		return null;
	}

	/**
	 * 通过主键ID查询数据
	 * 
	 * @param value
	 * @return
	 */
	public SuperClass findByPrimaryKey(Object value) {
		Page page = this.findByFieldEquals("id", value, 0, 0);
		if (!page.getResults().isEmpty()) {
			return (SuperClass) page.getResults().get(0);
		}
		return null;
	}

	public void flushSession() {
		this.getSession().flush();
	}


}
